﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using System.Windows;
using System.Collections.ObjectModel;
using Microsoft.Research.DynamicDataDisplay.Charts;

namespace Microsoft.Research.DynamicDataDisplay.DirectX2D
{
    public class DirectXPointsGraphBase : DirectXChart, IOneDimensionalChart
    {
        public DirectXPointsGraphBase()
        {
            Viewport2D.SetIsContentBoundsHost(this, true);
        }

        #region DataSource

        public IPointDataSource DataSource
        {
            get { return (IPointDataSource)GetValue(DataSourceProperty); }
            set { SetValue(DataSourceProperty, value); }
        }

        public static readonly DependencyProperty DataSourceProperty =
            DependencyProperty.Register(
              "DataSource",
              typeof(IPointDataSource),
              typeof(DirectXPointsGraphBase),
              new FrameworkPropertyMetadata
              {
                  AffectsRender = true,
                  DefaultValue = null,
                  PropertyChangedCallback = OnDataSourceChangedCallback
              }
            );

        private static void OnDataSourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DirectXPointsGraphBase graph = (DirectXPointsGraphBase)d;
            if (e.NewValue != e.OldValue)
            {
                graph.DetachDataSource(e.OldValue as IPointDataSource);
                graph.AttachDataSource(e.NewValue as IPointDataSource);
            }
            graph.OnDataSourceChanged(e);
        }

        private void AttachDataSource(IPointDataSource source)
        {
            if (source != null)
            {
                source.DataChanged += OnDataChanged;
            }
        }

        private void DetachDataSource(IPointDataSource source)
        {
            if (source != null)
            {
                source.DataChanged -= OnDataChanged;
            }
        }

        private void OnDataChanged(object sender, EventArgs e)
        {
            OnDataChanged();
        }

        protected virtual void OnDataChanged()
        {
            UpdateBounds(DataSource);

            RaiseDataChanged();
            Update();

            _renderUpdate = true;

            //if (DxHost != null)
            //{
            //    //DxHost.InvalidateVisual();
            //    DxHost.RenderUpdate = true;
            //}
        }

        private void RaiseDataChanged()
        {
            if (DataChanged != null)
            {
                DataChanged(this, EventArgs.Empty);
            }
        }
        public event EventHandler DataChanged;

        protected virtual void OnDataSourceChanged(DependencyPropertyChangedEventArgs args)
        {
            IPointDataSource newDataSource = (IPointDataSource)args.NewValue;
            if (newDataSource != null)
            {
                UpdateBounds(newDataSource);
            }

            Update();
        }

        private void UpdateBounds(IPointDataSource dataSource)
        {
            if (Plotter2D != null)
            {
                var transform = GetTransform();
                DataRect bounds = BoundsHelper.GetViewportBounds(dataSource.GetPoints(), transform.DataTransform);
                Viewport2D.SetContentBounds(this, bounds);
            }
        }

        #endregion

        #region DataTransform

        private DataTransform dataTransform = null;
        public DataTransform DataTransform
        {
            get { return dataTransform; }
            set
            {
                if (dataTransform != value)
                {
                    dataTransform = value;
                    Update();
                }
            }
        }

        protected CoordinateTransform GetTransform()
        {
            if (Plotter == null)
                return null;
            

            var transform = ((Plotter2D)Plotter).Viewport.Transform;
            if (dataTransform != null)
                transform = transform.WithDataTransform(dataTransform);

            return transform;
        }

        #endregion

        #region VisiblePoints

        public ReadOnlyCollection<Point> VisiblePoints
        {
            get { return GetVisiblePoints(this); }
            protected set { SetVisiblePoints(this, value); }
        }

        public static ReadOnlyCollection<Point> GetVisiblePoints(DependencyObject obj)
        {
            return (ReadOnlyCollection<Point>)obj.GetValue(VisiblePointsProperty);
        }

        public static void SetVisiblePoints(DependencyObject obj, ReadOnlyCollection<Point> value)
        {
            obj.SetValue(VisiblePointsProperty, value);
        }

        public static readonly DependencyProperty VisiblePointsProperty = DependencyProperty.RegisterAttached(
          "VisiblePoints",
          typeof(ReadOnlyCollection<Point>),
          typeof(DirectXPointsGraphBase),
          new FrameworkPropertyMetadata(null, OnVisiblePointsChanged));

        private static void OnVisiblePointsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DirectXPointsGraphBase graph = d as DirectXPointsGraphBase;
            if (graph != null)
            {
                graph.RaiseVisiblePointsChanged();
            }
        }

        public event EventHandler VisiblePointsChanged;
        protected void RaiseVisiblePointsChanged()
        {
            VisiblePointsChanged.Raise(this);
        }

        private bool provideVisiblePoints = false;
        public bool ProvideVisiblePoints
        {
            get { return provideVisiblePoints; }
            set
            {
                provideVisiblePoints = value;
                UpdateCore();
            }
        }

        #endregion

        protected IEnumerable<Point> GetPoints()
        {
            return DataSource.GetPoints(GetContext());
        }

        private readonly DataSource2dContext context = new DataSource2dContext();
        protected DependencyObject GetContext()
        {
            //context.VisibleRect = Plotter2D.Viewport.Visible;
            //context.ScreenRect = Plotter2D.Viewport.Output;

            return context;
        }

        protected void Update()
        {
            UpdateCore();
        }
    }
}
